#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <gsl/gsl_errno.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include "gsl/gsl_linalg.h"



#include <map>
#include <vector>
#include <algorithm>

using namespace std;
int debug;


class Celula
{
public:

   int id;     //Identificador de la celula
   double x;   //Coordanada x
   double y;   //Coordenada y
   double z;   //Coordenada z
   int f;      //el tipo, puede ser f=0 => normal; f=1 => incierto; f=2 => anormal
   string linea;
   Celula(double xx, double yy, double zz, int ff)
   {
      x=xx;
      y=yy;
      z=zz;
      f=ff;
   }
   Celula(){;}
};

int split(vector<string>& v, const string& str, char c)
{
v.clear();
string::const_iterator s = str.begin();
while (true) {
  string::const_iterator begin = s;

  while (*s != c && s != str.end()) { ++s; }

    v.push_back(string(begin, s));
    if (s == str.end()) {
        break;
    }
    if (++s == str.end()) {
       v.push_back("");
       break;
    }
  }
  return v.size();
}

void obtener (gsl_matrix *A,int n,int p){

ofstream log("log/matriz_A.log",ios::out);

for(int i=0;i<n;i++){
   for(int j=0;j<p;j++){
      
      log << gsl_matrix_get( A, i, j) << ",";
   }
   log << endl;

}



}

double distancia(Celula a, Celula b){
double d,xd,yd,zd;
xd=a.x-b.x;
yd=a.y-b.y;
zd=a.z-b.z;
d=xd*xd+yd*yd+zd*zd;
d=sqrt(d);
return d;
}


map < int, vector<Celula> > estadisticas(map < int, vector<Celula> > cells){

map < int, vector<Celula> >::iterator ci;
map <int, map<int,int> > tipos;
map <int, map<int,int> >::iterator ti;
map<int,int>::iterator ti2;

vector<Celula>::iterator vi;

ofstream est("estadisticas.log",ios::out);

est << "id,Tamano,f:tam" << endl;

  for(ci=cells.begin();ci!=cells.end();ci++){



	for(vi=ci->second.begin();vi!=ci->second.end();vi++){

            tipos[ci->first][vi->f]++;

	}


  }

  for(ti=tipos.begin();ti!=tipos.end();ti++){

	est << ti->first << ",";

	for(ti2=ti->second.begin();ti2!=ti->second.end();ti2++){

		est << ti2->first << ":" << ti2->second << ",";

	}
est << endl;
  }

return cells;
}


int main(int argc, char *argv[ ]){

debug=0;

//int p=20;
int p=54;
int q=3;
float umbral_afinidad=0.001;
string stipos="0";
double umbral_distancia=0.001;

string linea;
vector<string> vec(3);
map <string,string> files;

int aux=0;
int f=0;

ifstream data;
ifstream tipos;
ifstream archivos;
if(debug==1)
cout << __LINE__ << endl;

if(argc<1){

cout << "./seleccion -a <archivo>" << endl << endl;
cout << "<archivo>: localizacion del archivo de datos, localizacion archivo de tipos" << endl;

return -1;

}


for(int i = 0; i < argc; i++){
 if(!strcmp(argv[i],"-a")){
    f=i+1;


    archivos.open(argv[f]);

    cout << "Archivo: " << argv[f] << endl;
    if(!archivos){
      
      cout << "Los archivos del motor no se han definido. " << argv[f] << endl;
      cout << "Uso del sistema ./seleccion -a <archivo_de_archivos>" << endl;
      cout << "Donde <archivo_de_archivos> es un archivo formado por: " << endl << endl;
      cout << "archivo_de_datos" << endl;
      return -1;
      

    }

 }

}

aux=0;
while (! archivos.eof() ){

    getline (archivos,linea);
    if(!strcmp(linea.c_str(),""))
    {
	break;
    }


    files[linea]="1";
    aux++;

//    cout << "AUX: " << aux << endl;
}



int tam_datos=0;
//map <int,string> todos;
map <string,int> todos2;
cout << __LINE__ << endl;
aux=0;
int aux2=0;
//map <int,string> datos;

map <int,string> tipos_default;
int tam_files=files.size();

map<int,int> tipos_todos;
map<string,string>::iterator iter_files;

int i=0;
for(iter_files=files.begin();iter_files!=files.end();iter_files++){


if(debug==1)
cout << iter_files->first.c_str() << endl; 
data.open(iter_files->first.c_str());
string aux_ss;
stringstream ss;
while (! data.eof() ){


  getline (data,linea);

  split(vec,linea,';');

//  cout << __LINE__ << endl;

  //datos[i]=vec[0];
  tipos_default[atoi(vec[1].c_str())]="";

ss << vec[0].c_str() << ";" << vec[1].c_str();
aux_ss=ss.str();
  //todos[i]=linea;
  todos2[aux_ss]=i;

  ss.str("");

//ss.clear;
  //cout << i << endl;

  i++;


}
//  tam_datos=datos.size() + tam_datos;

  data.close();

}

int tamano=todos2.size();
cout << __LINE__ << endl;

cout << "Tamano de datos " << i << endl;
cout << "Tamano sin repetidos " << tamano << endl;


cout << __LINE__ << endl;

map <int,string>::iterator iter_todos;
map <string,int>::iterator iter_todos2;

ofstream salida_datos("motor.datos",ios::out);
ofstream salida_tipos("motor.tipos",ios::out);

cout << __LINE__ << endl;
gsl_vector *work,*V_S;
gsl_matrix *A,*R, *S, *Original,*y;

A = gsl_matrix_alloc( tamano, p );


Original = gsl_matrix_alloc( tamano, p ); 

cout << __LINE__ << endl;

vector <string> vec_datos(p);
int j=0;
Celula cell;

vector<Celula> celus;

for(iter_todos2=todos2.begin();iter_todos2!=todos2.end();iter_todos2++){

    cell.id=j;
    

    split(vec,iter_todos2->first,';');


    cell.linea=iter_todos2->first;

    split(vec_datos,vec[0],',');      

    

    if(!strcmp("-",vec[1].c_str())){

        tipos_todos[j]=0;
	cell.f=0;

    }else{


        tipos_todos[j]=atoi(vec[1].c_str());
	cell.f=atoi(vec[1].c_str());

    }

    for(int k=0;k<p;k++){

      gsl_matrix_set( A, j, k, atof(vec_datos[k].c_str()) );

    }
    celus.push_back(cell);
    j++;
}
//listo

cout << __LINE__ << endl;
//obtener(A,tamano,p);
gsl_matrix_memcpy(Original, A);


cout << __LINE__ << endl;
V_S = gsl_vector_alloc(p);
y = gsl_matrix_alloc( tamano, q );
R=gsl_matrix_alloc(p,p);


cout << __LINE__ << endl;


cout << "Calculando SVD" << endl;
if(  gsl_linalg_SV_decomp_jacobi( A, R, V_S)  ){
	gsl_vector_free( work );
	gsl_vector_free(V_S);
	gsl_matrix_free( A );
	gsl_matrix_free( R );
	exit( EXIT_FAILURE );
}


//obtener(A,tamano,p);
cout << "LISTO!" << endl;
cout << __LINE__ << endl;
map < int, vector<Celula> > cells;
map < int, vector<Celula> > cells2;

map < int, vector<Celula> > original_cells;
map < int, vector<Celula> > copia_cells;


cout << __LINE__ << endl;
for(int t=0; t < celus.size(); t++){

celus[t].x=gsl_matrix_get(A,t,0);
celus[t].y=gsl_matrix_get(A,t,1);
celus[t].z=gsl_matrix_get(A,t,2);

cells[t].push_back(celus[t]);


}



/*
int t=0;
for(iter_todos2=todos2.begin();iter_todos2!=todos2.end();iter_todos2++){

  cell.id=iter_todos2->second;
  cell.x=gsl_matrix_get(A,t,0);
  cell.y=gsl_matrix_get(A,t,1);
  cell.z=gsl_matrix_get(A,t,2);
  cell.linea=iter_todos2->first;

  cell.f=tipos_todos[iter_todos2->second];
  cells[t].push_back(cell);

  t++;
}
*/
if(debug==1)
cout << __LINE__ << endl;
copia_cells=cells;
original_cells=cells;
ofstream log_apoptosis("log/apoptosis.log",ios::out);
float xd,yd,zd,distancia_cercana,dist_aux;
Celula mas_cercana;


cout << __LINE__ << endl;
map<int, vector<Celula> >::iterator iter,iter2,iter_aux;


for(iter=cells.begin();iter!=cells.end();iter++){

if(debug==1){
  log_apoptosis << "iter->first " << iter->first << endl;
  log_apoptosis << "iter->second.id " << iter->second[0].id << endl;
  log_apoptosis << "iter->second.f " << iter->second[0].f << endl;
}

  distancia_cercana=1000;


  for(iter2=cells.begin();iter2!=cells.end();iter2++){

     if(iter->second[0].id!=iter2->second[0].id ){

         xd=iter2->second[0].x-iter->second[0].x;
         yd=iter2->second[0].y-iter->second[0].y;
         zd=iter2->second[0].z-iter->second[0].z;
         dist_aux=xd*xd+yd*yd+zd*zd;
         dist_aux=sqrt(dist_aux);

         if(dist_aux<distancia_cercana ){
              mas_cercana=iter2->second[0];
              distancia_cercana=dist_aux;

         }
     }
  }

if(debug==1){
   log_apoptosis << "i: " << iter->first << "\n x: " << iter->second[0].x << " y: " << iter->second[0].y << " z: " << iter->second[0].z <<" f: " << iter->second[0].f << endl;

   log_apoptosis << "mas_cercana: " << mas_cercana.id << "\n x: " << mas_cercana.x << " y: " << mas_cercana.y << " z: " << mas_cercana.z <<" f: " << mas_cercana.f << endl;

   log_apoptosis << "Los mas cercano " << iter->second[0].id << "-" << mas_cercana.id << " | " << distancia_cercana  << endl;
}

   if(distancia_cercana<umbral_afinidad && iter->second[0].f == mas_cercana.f){

if(debug==1)
       log_apoptosis << "Eliminar: " << iter->second[0].id  << endl;
       cells.erase(iter);

   }
}


int cont=0;
for(iter=cells.begin();iter!=cells.end();iter++){


if(debug==1)
log_apoptosis << cont << "," << iter->second[0].id << "," << iter->second[0].f << endl;


cont++;
}


cout << __LINE__ << endl;


ofstream log_inmune("log/inmunizacion.log",ios::out);

for(iter=cells.begin();iter!=cells.end();iter++){


  distancia_cercana=1000.0;
    
	          
  for(iter2=copia_cells.begin();iter2 != copia_cells.end();iter2++){
		                
     if( iter->first!=iter2->first){
          xd=(iter2->second)[0].x-(iter->second)[0].x;
          yd=(iter2->second)[0].y-(iter->second)[0].y;
          zd=(iter2->second)[0].z-(iter->second)[0].z;
          dist_aux=xd*xd+yd*yd+zd*zd;
          dist_aux=sqrt(dist_aux);
						                                   
          if(dist_aux<distancia_cercana){

              iter_aux=iter2;
              distancia_cercana=dist_aux;
          }
     }
  }


  if( distancia_cercana<umbral_afinidad && (iter->second)[0].f != (iter_aux->second)[0].f ){

if(debug==1)
       log_inmune << "Reintegrar: " << iter_aux->second[0].id << "," << iter_aux->second[0].f   << endl;
       cells[iter_aux->first].push_back((iter_aux->second)[0]);



   }




}
copia_cells.erase(copia_cells.begin(),copia_cells.end());

cells2=cells;
cout << __LINE__ << endl;

double aaa;

map < int, vector<Celula> >::iterator ce;
map < int, vector<Celula> >::iterator co;

for(ce=cells2.begin(); ce!=cells2.end(); ce++){

//cout << __LINE__ << endl;
  for(co=original_cells.begin();co!= original_cells.end();co++){

//cout << __LINE__ << endl;

aaa=distancia(ce->second[0],co->second[0]);

//cout << __LINE__ << endl;
   if(aaa<umbral_distancia){

//cout << __LINE__ << endl;
	ce->second.push_back(co->second[0]);

//cout << __LINE__ << endl;
   }

  }
}

original_cells.erase(original_cells.begin(),original_cells.end());
cout << __LINE__ << endl;
estadisticas(cells2);
cells2.erase(cells2.begin(),cells2.end());
cout << __LINE__ << endl;
//exit;




cont=0;
for(iter=cells.begin();iter!=cells.end();iter++){


if(debug==1)
log_inmune << cont << "," << iter->second[0].id << "," << iter->second[0].f << endl;


cont++;
}

cout << __LINE__ << endl;

ofstream tipos_d("tipos_archivo.dat",ios::out);


map <int,string>::iterator tip;

for( tip=tipos_default.begin();tip!=tipos_default.end();tip++){

    tipos_d << tip->first << endl;

}


cout << __LINE__ << endl;
ofstream log_id("log/id.log",ios::out);
ofstream motor_datos("motor.todo",ios::out);
string aux_s;
for(iter=cells.begin();iter!=cells.end();iter++){

   aux_s="";
/*
   for(iter_todos2=todos2.begin();iter_todos2!=todos2.end();iter_todos2++){

      if(iter_todos2->second==iter->second[0].id){
          aux_s=iter_todos2->first;
      }

   }

   if(!strcmp(aux_s.c_str(),"")){
      cout << "ERROR: No se encontro string" << endl;

   }
*/
    motor_datos << iter->second[0].linea << endl;
   
    split(vec,iter->second[0].linea,';');
    //split(vec,todos2[iter->second[0].id],';');

if(debug==1)
    log_id << iter->second[0].id  << "," << iter->second[0].f << " - " << iter->second[0].linea << endl;

    salida_datos << vec[0] << endl;
    salida_tipos << vec[1] << endl;

}


cout << __LINE__ << endl;


//return 1;

}
